package com.encrypt.encrptspringbootstarter.interceptor;



import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

/**
 * 用于处理请求实体字段加密《select/update/insert》
 */
@Component
@Intercepts({
        @Signature(type = ParameterHandler.class, method = "setParameters", args = {PreparedStatement.class})
})
public class EntityInterceptor implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(ResultSetInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            // @Signature 指定了 type= parameterHandler 后，这里的 invocation.getTarget()
            // 便是parameterHandler
            // 若指定ResultSetHandler ，这里则能强转为ResultSetHandler
            ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
            // 获取参数对像，即 mapper 中 paramsType 的实例
            Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
            parameterField.setAccessible(true);
            // 取出实例
            Object parameterObject = parameterField.get(parameterHandler);
            if (parameterObject != null) {

                Class<?> parameterObjectClass = parameterObject.getClass();
                HasSensitiveData annotation = parameterObjectClass.getAnnotation(HasSensitiveData.class);
                if (Objects.isNull(annotation)) {
                    return invocation.proceed();
                }
                // 取出当前当前类所有字段，传入加密方法
                Field[] declaredFields = parameterObjectClass.getDeclaredFields();
                encrypt(declaredFields, parameterObject);
            }
        } catch (RuntimeException e) {
            log.error("加密参数处理发生异常："+e.getMessage(),e);
        }
        return invocation.proceed();
    }

    public <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {
        for (Field field : declaredFields) {
            FieldPartialEncryption fieldPartialEnCryption = field.getAnnotation(FieldPartialEncryption.class);
            if (!Objects.isNull(fieldPartialEnCryption)) {
                KeyWordsEnum keyWordsEnum = fieldPartialEnCryption.keyWords();
                String key= fieldPartialEnCryption.key();
                String value= fieldPartialEnCryption.value();
                if (StringUtils.isBlank(key) || StringUtils.isBlank(value)||StringUtils.isBlank(keyWordsEnum.getKeywords()) ){
                    continue;
                }
                if(field.getName().equals(key)){
                    List<String> keywords = Arrays.asList(keyWordsEnum.getKeywords().split(","));
                    Object keyObject = null;
                    try {
                        field.setAccessible(true);
                        keyObject = field.get(paramsObject);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    // 查看key字段是否包含关键字，有则加密value字段
                    // 暂时只实现String类型的加密
                    if (keyObject instanceof String) {
                        String finalObject = (String)keyObject;
                        boolean b = keywords.stream().noneMatch(e -> finalObject.contains(e));
                        if(b){
                            continue;
                        }
                    }
                    Arrays.stream(declaredFields).filter(e->e.getName().equals(value)).forEach(f->{
                        f.setAccessible(true);
                        Object valueObject = null;
                        try {
                            valueObject = f.get(paramsObject);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                        // 暂时只实现String类型的加密
                        if (valueObject instanceof String) {
                            String value2 = (String) valueObject;
                            // 加密
                            try {
                                f.set(paramsObject, AESEncryption.encrypt(value2));
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }
            // 取出所有被EncryptTransaction注解的字段
            FieldAllEncryption encryptTransaction = field.getAnnotation(FieldAllEncryption.class);
            if (Objects.isNull(fieldPartialEnCryption) && !Objects.isNull(encryptTransaction)) {
                field.setAccessible(true);
                Object object = field.get(paramsObject);
                // 暂时只实现String类型的加密
                if (object instanceof String) {
                    String value = (String) object;
                    // 加密
                    try {
                        field.set(paramsObject, AESEncryption.encrypt(value));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return paramsObject;
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
    @PostConstruct
    public void init() {
        System.out.println("MyBatisConfig has been initialized");
    }
}